#!/usr/bin/env python
#     Copyright 2015, Kay Hayen, mailto:kay.hayen@gmail.com
#
#     Part of "Nuitka", an optimizing Python compiler that is compatible and
#     integrates with CPython, but also works on its own.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
#

from __future__ import print_function

import sys, os, shutil

from redbaron import RedBaron

print("Updating", sys.argv[1])

with open(sys.argv[1], "r") as source_code:
    red = RedBaron(source_code.read().rstrip()+"\n")

def updateCall(call_node):
    max_len = 0
    for argument in call_node:
        if argument.type == "argument_generator_comprehension":
            return

        if argument.target is not None:
            key = argument.target.value
        else:
            key = None

        if key is not None:
            max_len = max(max_len, len(key))

    if "\n" not in call_node.second_formatting.dumps():
        del call_node.second_formatting[:]
        del call_node.third_formatting[:]

    for argument in call_node:
        if argument.target is not None:
            key = argument.target.value
        else:
            key = None

        if key is not None:
            if not argument.second_formatting:
                argument.second_formatting = " "

            if "\n" in str(call_node.second_formatting):
                if len(argument.first_formatting) > 0:
                    spacing = argument.first_formatting[0].value
                else:
                    spacing = ""

                if len(key)+len(spacing) != max_len + 1:
                    argument.first_formatting = " " * (max_len - len(key) + 1)
            else:
                argument.first_formatting = " "
        else:
            if "\n" not in str(call_node.second_formatting):
                if argument.value.type in ("string", "binary_string", "raw_string"):
                    argument.value.second_formatting = ""

def updateTuple(tuple_node):
    if "\n" not in str(tuple_node.dumps()):
        tuple_node.second_formatting = ""
        tuple_node.third_formatting = ""

        if tuple_node.with_parenthesis:
            if len(tuple_node.value.node_list) > 0:
                tuple_node.value.node_list[-1].second_formatting = ""

        for argument in tuple_node.value:
            if argument.type in ("string", "binary_string", "raw_string"):
                argument.second_formatting = ""

def updateString(string_node):
    # Skip doc strings for now.
    if string_node.parent.type in ("class", "def", None):
        return

    value = string_node.value

    def isQuotedWith(quote):
        return value.startswith(quote) and value.endswith(quote)

    for quote in "'''", '"""', "'", '"':
        if isQuotedWith(quote):
            break
    else:
        assert False, value

    real_value = value[len(quote):-len(quote)]
    assert quote + real_value + quote == value

    if "\n" not in real_value:
        # Single characters, should be quoted with "'"
        if len(eval(value)) == 1:
            if real_value != "'":
                string_node.value = "'" + real_value + "'"
        else:
            if '"' not in real_value:
                string_node.value = '"' + real_value + '"'

for node in red.find_all("CallNode"):
    try:
        updateCall(node)
    except Exception:
        print("Problem with", node)
        node.help(deep = True, with_formatting = True)
        raise

for node in red.find_all("TupleNode"):
    try:
        updateTuple(node)
    except Exception:
        print("Problem with", node)
        node.help(deep = True, with_formatting = True)
        raise

for node in red.find_all("ListNode"):
    try:
        updateTuple(node)
    except Exception:
        print("Problem with", node)
        node.help(deep = True, with_formatting = True)
        raise

for node in red.find_all("StringNode"):
    try:
        updateString(node)
    except Exception:
        print("Problem with", node)
        node.help(deep = True, with_formatting = True)
        raise

for node in red.find_all("DefNode"):
    # print("1:", repr(str(node.first_formatting)))
    # print("2:", repr(str(node.second_formatting)))
    #print("3:", repr(str(node.third_formatting)))
    # print("4:", repr(str(node.fourth_formatting)))
    pass
    # TODO: Third and fourth formatting cannot be set.
    # assert False, node.arguments

new_name = sys.argv[1] + ".new"

with open(new_name, "w") as source_code:
    source_code.write(red.dumps())

# There is no way to safely replace a file on Windows, but lets try on Linux
# at least.
old_stat = os.stat(sys.argv[1])

try:
    os.rename(new_name, sys.argv[1])
except OSError:
    shutil.copy(new_name, sys.argv[1])

os.chmod(sys.argv[1], old_stat.st_mode )
